#include "BasicRepresentationEngine.hpp"

const float BasicRepresentationEngine::CAMERA_DEPTH = 20.0f;

BasicRepresentationEngine::BasicRepresentationEngine(GameEngine* gameEngine) : RepresentationEngine(gameEngine) {
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    // Textures.
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // Fonts.
    this->font = new glfont::GLFont();
    this->font->Create("fonts/menu.glf", 1);
}

BasicRepresentationEngine::~BasicRepresentationEngine() {
    this->font->Destroy();
    delete this->font;
}

void BasicRepresentationEngine::init(Menu* menu) {
}

void BasicRepresentationEngine::init(Scene* scene) {
}

void BasicRepresentationEngine::render(Menu* menu, Scene* scene) {
    if (scene == NULL) {
        glClear(GL_COLOR_BUFFER_BIT);
    }
    else {
        this->render(scene, false);
    }

    glEnable(GL_TEXTURE_2D);
    glLoadIdentity();
    glTranslatef(0.0f, 0.0f, -BasicRepresentationEngine::CAMERA_DEPTH);

    this->font->Begin();
    int numberOfOptions = menu->options->size();

    for (int i = 0; i < numberOfOptions; ++i) {
        if (i == menu->selectedOption) {
            glColor3f(1.0f, 1.0f, 0.0f);
        }
        else {
            glColor3f(1.0f, 1.0f, 1.0f);
        }

        Menu::MenuEntry* entry = menu->options->at(i);
        this->font->DrawString(entry->label->c_str(), 0.025f, -10.0f, -7.5f + numberOfOptions - i);
    }

    glutSwapBuffers();
}

void BasicRepresentationEngine::render(Scene* scene) {
    this->render(scene, true);
}

void BasicRepresentationEngine::render(Scene* scene, bool swapBuffers) {
    glDisable(GL_TEXTURE_2D);
    glClear(GL_COLOR_BUFFER_BIT);
    glLoadIdentity();
    b2Vec2 heroPosition = scene->hero->body->GetPosition();
    glTranslatef(-heroPosition.x, -heroPosition.y, -BasicRepresentationEngine::CAMERA_DEPTH);

    for (vector<Element*>::iterator i = scene->elements->begin(); i != scene->elements->end(); ++i) {
        this->represent(*i);
    }

    if (swapBuffers) {
        glutSwapBuffers();
    }
}

void BasicRepresentationEngine::represent(Element* element) {
    // Choosing color for shapes.
    float color[4];
    color[3] = 1.0f;

    if (element->isRoom() || element->isPlatform() || element->isMoveablePlatform()) {
        color[0] = 1.0f;
        color[1] = 1.0f;
        color[2] = 1.0f;
    }
    else if (element->isBox()) {
        color[0] = 0.5f;
        color[1] = 0.5f;
        color[2] = 0.5f;
    }
    else if (element->isHero()) {
        color[0] = 0.0f;
        color[1] = 0.5f;
        color[2] = 0.0f;
    }
    else if (element->isSpotlight()) {
        // Drawing light.
        SpotlightElement* spotlight = (SpotlightElement*)element;
        color[3] = 0.5f;

        if (spotlight->light->lightType == LightElement::attractiveLight) {
            color[0] = 1.0f;
            color[1] = 0.0f;
            color[2] = 0.0f;
        }
        else if (spotlight->light->lightType == LightElement::repulsiveLight) {
            color[0] = 0.0f;
            color[1] = 0.0f;
            color[2] = 1.0f;
        }
        else if (spotlight->light->lightType == LightElement::solidLight) {
            color[0] = 1.0f;
            color[1] = 0.5f;
            color[2] = 0.0f;
        }
        else if (spotlight->light->lightType == LightElement::elasticLight) {
            color[0] = 1.0f;
            color[1] = 0.0f;
            color[2] = 1.0f;
        }
        else if (spotlight->light->lightType == LightElement::slipperyLight) {
            color[0] = 1.0f;
            color[1] = 1.0f;
            color[2] = 0.0f;
        }

        this->represent(spotlight->light->body, color);

        // Drawing spotlight.
        color[0] = 0.25f;
        color[1] = 0.25f;
        color[2] = 0.25f;
    }
    else if (element->isSwitch()) {
        color[0] = 0.5f;
        color[1] = 0.0f;
        color[2] = 0.0f;
    }
    else if (element->isDoor()) {
        color[0] = 1.0f;
        color[1] = 1.0f;
        color[2] = 0.0f;
    }
    else if (element->isExit()) {
        color[0] = 0.0f;
        color[1] = 0.0f;
        color[2] = 0.5f;
    }
    else if (element->isFlash()) {
        color[0] = 0.0f;
        color[1] = 1.0f;
        color[2] = 1.0f;
    }

    this->represent(element->body, color);
}

void BasicRepresentationEngine::represent(b2Body* body, float color[4]) {
    glPushMatrix();
    b2Vec2 position = body->GetPosition();
    glTranslatef(position.x, position.y, 0.0f);
    glRotatef(body->GetAngle() * 180.0f / b2_pi, 0, 0, 1.0f);

    // Drawing shapes.

    if (body->IsActive()) {
        glColor4f(color[0], color[1], color[2], color[3]);
        b2Fixture* fixture = body->GetFixtureList();

        while (fixture != NULL) {
            b2Shape* shape = fixture->GetShape();
            b2Shape::Type shapeType = shape->GetType();

            if (shapeType == b2Shape::e_polygon) {
                glBegin(GL_POLYGON);

                b2PolygonShape* polygonShape = (b2PolygonShape*)shape;
                int vertexCount = polygonShape->GetVertexCount();

                for (int i = 0; i < vertexCount; ++i) {
                    b2Vec2 vertex = polygonShape->GetVertex(i);
                    glVertex2f(vertex.x, vertex.y);
                }

                glEnd();
            }
            else if (shapeType == b2Shape::e_chain) {
                glBegin(GL_LINES);

                b2ChainShape* chainShape = (b2ChainShape*)shape;
                int edgeCount = chainShape->GetChildCount();
                b2EdgeShape edgeShape;

                for (int i = 0; i < edgeCount; ++i) {
                    chainShape->GetChildEdge(&edgeShape, i);
                    glVertex2f(edgeShape.m_vertex1.x, edgeShape.m_vertex1.y);
                    glVertex2f(edgeShape.m_vertex2.x, edgeShape.m_vertex2.y);
                }

                glEnd();
            }
            else if (shapeType == b2Shape::e_edge) {
                glBegin(GL_LINES);
                b2EdgeShape* edgeShape = (b2EdgeShape*)shape;
                glVertex2f(edgeShape->m_vertex1.x, edgeShape->m_vertex1.y);
                glVertex2f(edgeShape->m_vertex2.x, edgeShape->m_vertex2.y);
                glEnd();
            }

            fixture = fixture->GetNext();
        }
    }

    // Drawing position.
    glColor3f(1.0f, 1.0f, 0.0f);

    glBegin(GL_LINES);
    glVertex2f(-0.1f, 0.0f);
    glVertex2f(0.1f, 0.0f);
    glVertex2f(0.0f, -0.1f);
    glVertex2f(0.0f, 0.1f);
    glEnd();

    glPopMatrix();
}
